package com.binance.notification.web.push.async;

import java.util.List;

import com.binance.notification.web.push.RequestCallback;
import com.google.gson.JsonParseException;
import com.google.gson.JsonParser;

import cn.jiguang.common.ClientConfig;
import cn.jiguang.common.ServiceHelper;
import cn.jiguang.common.connection.HttpProxy;
import cn.jiguang.common.connection.NativeHttpClient;
import cn.jiguang.common.resp.APIConnectionException;
import cn.jiguang.common.resp.APIRequestException;
import cn.jiguang.common.resp.BaseResult;
import cn.jiguang.common.resp.ResponseWrapper;
import cn.jiguang.common.utils.Preconditions;
import cn.jpush.api.push.CIDResult;
import cn.jpush.api.push.model.PushPayload;

public class AsyncPushClient {
    private AsyncApacheHttpClient _asyncHttpClient;
    private NativeHttpClient _httpClient;
    private String _baseUrl;
    private String _pushPath;
    private String _pushValidatePath;

    private JsonParser _jsonParser = new JsonParser();

    // If not present, true by default.
    private int _apnsProduction;

    // If not present, the default value is 86400(s) (one day)
    private long _timeToLive;

    /**
     * Create a Push Client.
     *
     * @param masterSecret API access secret of the appKey.
     * @param appKey The KEY of one application on JPush.
     */
    public AsyncPushClient(String masterSecret, String appKey) {
        this(masterSecret, appKey, null, ClientConfig.getInstance());
    }

    /**
     * This will be removed in the future. Please use ClientConfig{jiguang-common
     * cn.jiguang.common.ClientConfig} instead of this constructor.
     *
     * @param masterSecret API access secret of the appKey.
     * @param appKey The KEY of one application on JPush.
     * @param maxRetryTimes The max retry times.
     */
    @Deprecated
    public AsyncPushClient(String masterSecret, String appKey, int maxRetryTimes) {
        this(masterSecret, appKey, maxRetryTimes, null);
    }

    /**
     * Create a Push Client with max retry times. This will be removed in the future. Please use
     * ClientConfig{jiguang-common cn.jiguang.common.ClientConfig} instead of this constructor.
     *
     * @param masterSecret API access secret of the appKey.
     * @param appKey The KEY of one application on JPush.
     * @param maxRetryTimes max retry times
     * @param proxy The max retry times.
     */
    @Deprecated
    public AsyncPushClient(String masterSecret, String appKey, int maxRetryTimes, HttpProxy proxy) {
        ServiceHelper.checkBasic(appKey, masterSecret);

        ClientConfig conf = ClientConfig.getInstance();
        conf.setMaxRetryTimes(maxRetryTimes);

        this._baseUrl = (String) conf.get(ClientConfig.PUSH_HOST_NAME);
        this._pushPath = (String) conf.get(ClientConfig.PUSH_PATH);
        this._pushValidatePath = (String) conf.get(ClientConfig.PUSH_VALIDATE_PATH);

        this._apnsProduction = (Integer) conf.get(ClientConfig.APNS_PRODUCTION);
        this._timeToLive = (Long) conf.get(ClientConfig.TIME_TO_LIVE);

        String authCode = ServiceHelper.getBasicAuthorization(appKey, masterSecret);
        this._asyncHttpClient = new AsyncApacheHttpClient(authCode, proxy, conf);
    }

    public AsyncPushClient(String masterSecret, String appKey, HttpProxy proxy, ClientConfig conf) {
        ServiceHelper.checkBasic(appKey, masterSecret);

        this._baseUrl = (String) conf.get(ClientConfig.PUSH_HOST_NAME);
        this._pushPath = (String) conf.get(ClientConfig.PUSH_PATH);
        this._pushValidatePath = (String) conf.get(ClientConfig.PUSH_VALIDATE_PATH);

        this._apnsProduction = (Integer) conf.get(ClientConfig.APNS_PRODUCTION);
        this._timeToLive = (Long) conf.get(ClientConfig.TIME_TO_LIVE);

        String authCode = ServiceHelper.getBasicAuthorization(appKey, masterSecret);
        this._asyncHttpClient = new AsyncApacheHttpClient(authCode, proxy, conf);

    }

    /**
     * Create a Push Client with global settings.
     * <p>
     * If you want different settings from default globally, this constructor is what you needed.
     * This will be removed in the future. Please use ClientConfig{jiguang-common
     * cn.jiguang.common.ClientConfig} instead of this constructor.
     *
     * @param masterSecret API access secret of the appKey.
     * @param appKey The KEY of one application on JPush.
     * @param apnsProduction Global APNs environment setting. It will override PushPayload Options.
     * @param timeToLive Global time_to_live setting. It will override PushPayload Options.
     */
    @Deprecated
    public AsyncPushClient(String masterSecret, String appKey, boolean apnsProduction, long timeToLive) {
        this(masterSecret, appKey);
        if (apnsProduction) {
            _apnsProduction = 1;
        } else {
            _apnsProduction = 0;
        }
        this._timeToLive = timeToLive;
    }

    /**
     * This will be removed in the future. Please use ClientConfig{jiguang-common
     * cn.jiguang.common.ClientConfig#setGlobalPushSetting} instead of this method.
     *
     * @param apnsProduction Global APNs environment setting. It will override PushPayload Options.
     * @param timeToLive Global time_to_live setting. It will override PushPayload Options.
     */
    @Deprecated
    public void setDefaults(boolean apnsProduction, long timeToLive) {
        if (apnsProduction) {
            _apnsProduction = 1;
        } else {
            _apnsProduction = 0;
        }
        this._timeToLive = timeToLive;
    }

    public void setBaseUrl(String baseUrl) {
        this._baseUrl = baseUrl;
    }

    private String pushPayloadsToString(List<PushPayload> pushPayload) {
        Preconditions.checkArgument(!(null == pushPayload || pushPayload.size() == 0),
                "pushPayload should not be null");
        if (pushPayload.size() == 1) {
            Preconditions.checkArgument(null != pushPayload.get(0), "pushPayload should not be null");
            return pushPayload.get(0).toString();
        }
        StringBuilder sb = new StringBuilder(pushPayload.size() * 16);
        sb.append("[");
        for (PushPayload payload : pushPayload) {
            Preconditions.checkArgument(!(null == payload), "pushPayload should not be null");
            if (_apnsProduction > 0) {
                payload.resetOptionsApnsProduction(true);
            } else if (_apnsProduction == 0) {
                payload.resetOptionsApnsProduction(false);
            }

            if (_timeToLive >= 0) {
                payload.resetOptionsTimeToLive(_timeToLive);
            }
            sb.append(payload.toString());
            sb.append(",");
        }
        sb.deleteCharAt(sb.length() - 1);
        sb.append("]");

        return sb.toString();
    }

    public void sendPushPayload(RequestCallback callback, List<PushPayload> pushPayload) {
        String jsonStr = this.pushPayloadsToString(pushPayload);
        _asyncHttpClient.sendPost(_baseUrl + _pushPath, jsonStr, callback);
    }

    public void sendPushPayloadValidate(RequestCallback callback, List<PushPayload> pushPayload) {
        String jsonStr = this.pushPayloadsToString(pushPayload);
        _asyncHttpClient.sendPost(_baseUrl + _pushValidatePath, jsonStr, callback);

    }

    private String payloadStringsToString(List<String> payloadString) {
        Preconditions.checkArgument(payloadString != null && payloadString.size() > 0,
                "pushPayload should not be empty");
        if (payloadString.size() == 1) {
            try {
                _jsonParser.parse(payloadString.get(0));
            } catch (JsonParseException e) {
                Preconditions.checkArgument(false, "payloadString should be a valid JSON string.");
            }
            return payloadString.get(0);
        }
        StringBuilder sb = new StringBuilder(payloadString.size() * 16);
        sb.append("[");
        for (String payload : payloadString) {
            try {
                _jsonParser.parse(payload);
            } catch (JsonParseException e) {
                Preconditions.checkArgument(false, "payloadString should be a valid JSON string.");
            }
            sb.append(payload);
            sb.append(",");
        }
        sb.deleteCharAt(sb.length() - 1);
        sb.append("]");

        return sb.toString();
    }

    public void sendPushPayloadString(RequestCallback callback, List<String> payloadString) {
        String jsonStr = this.payloadStringsToString(payloadString);
        _asyncHttpClient.sendPost(_baseUrl + _pushPath, jsonStr, callback);

    }

    public void sendPushPayloadStringValidate(RequestCallback callback, List<String> payloadString) {
        String jsonStr = this.payloadStringsToString(payloadString);
        _asyncHttpClient.sendPost(_baseUrl + _pushValidatePath, jsonStr, callback);
    }

    /**
     * Get cid list, the data form of cid is appKey-uuid.
     *
     * @param count the count of cid list, from 1 to 1000. default is 1.
     * @param type default is "push", option: "schedule"
     * @return CIDResult, an array of cid
     * @throws APIConnectionException connect exception
     * @throws APIRequestException request exception
     */
    public CIDResult getCidList(int count, String type) throws APIConnectionException, APIRequestException {
        Preconditions.checkArgument(count >= 1 && count <= 1000, "count should not less than 1 or larger than 1000");
        Preconditions.checkArgument(type == null || type.equals("push") || type.equals("schedule"),
                "type should be \"push\" or \"schedule\"");
        ResponseWrapper responseWrapper;
        if (type != null) {
            responseWrapper = _httpClient.sendGet(_baseUrl + _pushPath + "/cid?count=" + count + "&type=" + type);
        } else {
            responseWrapper = _httpClient.sendGet(_baseUrl + _pushPath + "/cid?count=" + count);
        }
        return BaseResult.fromResponse(responseWrapper, CIDResult.class);
    }


}
